#include "smbus_postbox.h"

#include <algorithm>
#include <cctype>
#include <cstring>
#include <iomanip>
#include <mc/log.h>
#include <sstream>
#include <thread>

namespace dev {

namespace {
std::string bytes_to_hex(const std::string& s) {
    std::ostringstream oss;
    oss << std::hex << std::setfill('0');
    for (size_t i = 0; i < s.size(); ++i) {
        if (i) {
            oss << ' ';
        }
        oss << std::setw(2) << static_cast<unsigned>(static_cast<unsigned char>(s[i]));
    }
    return oss.str();
}

std::string vec_to_hex(const std::vector<uint8_t>& v) {
    std::ostringstream oss;
    oss << std::hex << std::setfill('0');
    for (size_t i = 0; i < v.size(); ++i) {
        if (i) {
            oss << ' ';
        }
        oss << std::setw(2) << static_cast<unsigned>(v[i]);
    }
    return oss.str();
}
} // namespace

std::shared_ptr<SMBusPostboxProtocol> SMBusPostboxProtocol::create() {
    return std::make_shared<SMBusPostbox>();
}

SMBusPostbox::SMBusPostbox(mc::engine::abstract_object* parent) : mc::core::object(parent) {
}

SMBusPostbox::~SMBusPostbox() = default;

void SMBusPostbox::init(uint8_t data_reg_addr) {
    data_reg_addr_ = data_reg_addr;
    elog("SMBusPostbox init, data_reg_addr: 0x${addr:02x}", ("addr", data_reg_addr_));
}

void SMBusPostbox::registerSMBus(mc::shared_ptr<smbus> smbus_dev) {
    smbus_dev_ = smbus_dev;
    elog("SMBusPostbox: smbus device registered");
}

// ============ 基础寄存器读写 ============

bool SMBusPostbox::writeNVGPUReg(uint32_t data, uint8_t offset) {
    if (!smbus_dev_) {
        elog("SMBusPostbox: smbus device not registered");
        return false;
    }

    // 将 32 位数据拆成 4 字节 (BE)
    std::string data_str;
    data_str.push_back(0x04); // 长度
    data_str.push_back(static_cast<char>((data >> 24) & 0xFF));
    data_str.push_back(static_cast<char>((data >> 16) & 0xFF));
    data_str.push_back(static_cast<char>((data >> 8) & 0xFF));
    data_str.push_back(static_cast<char>(data & 0xFF));

    // 打印发送的数据
    elog("SMBusPostbox TX writeNVGPUReg offset=0x${offset:02x}, data=${data}",
         ("offset", offset)("data", bytes_to_hex(data_str)));

    auto [ok, rsp] = smbus_dev_->send(offset, data_str);
    if (!ok) {
        elog("SMBusPostbox: writeNVGPUReg failed at 0x${offset:02x}", ("offset", offset));
        return false;
    }
    // 打印底层返回（如果有）
    if (!rsp.empty()) {
        elog("SMBusPostbox TX rsp offset=0x${offset:02x}, rsp=${rsp}",
             ("offset", offset)("rsp", vec_to_hex(rsp)));
    }
    return true;
}

std::vector<uint8_t> SMBusPostbox::readNVGPUReg(uint8_t offset) {
    if (!smbus_dev_) {
        elog("SMBusPostbox: smbus device not registered");
        return {};
    }

    auto [ok, data_vec] = smbus_dev_->read(offset, 5);
    if (!ok) {
        elog("SMBusPostbox: readNVGPUReg no data at 0x${offset:02x}", ("offset", offset));
        return {};
    }

    // 打印接收的原始data（包含长度字节）
    elog("SMBusPostbox RX readNVGPUReg offset=0x${offset:02x}, raw=${raw}",
         ("offset", offset)("raw", vec_to_hex(data_vec)));

    if (data_vec.size() < 1 || data_vec[0] != 4) {
        uint8_t first = data_vec.empty() ? 0 : data_vec[0];
        elog("SMBusPostbox: readNVGPUReg invalid len at 0x${offset:02x}, first=0x${first:02x}",
             ("offset", offset)("first", first));
        return {};
    }

    if (data_vec.size() != 5) {
        elog("SMBusPostbox: readNVGPUReg invalid total size at 0x${offset:02x}, size=${size}",
             ("offset", offset)("size", data_vec.size()));
        return {};
    }

    // 去掉长度字节，只返回 4 字节内容
    std::vector<uint8_t> out(data_vec.begin() + 1, data_vec.end());

    // 打印去掉长度后的payload
    elog("SMBusPostbox RX payload offset=0x${offset:02x}, payload=${payload}",
         ("offset", offset)("payload", vec_to_hex(out)));

    return out;
}

uint32_t SMBusPostbox::assembleSMBPBICommand(uint8_t opcode, uint8_t arg1, uint8_t arg2) {
    // cmd: opcode(8) | arg1(8) | arg2(8) | CMD_EXE_FLAG(0x80)
    return (static_cast<uint32_t>(opcode) << 24) | (static_cast<uint32_t>(arg1) << 16) |
           (static_cast<uint32_t>(arg2) << 8) | CMD_EXE_FLAG;
}

uint32_t SMBusPostbox::getMaskedValue(uint32_t raw, uint32_t mask) {
    if (mask == 0) {
        return raw;
    }
    uint32_t offset = 0;
    uint32_t m      = mask;
    while ((m & 1u) == 0u) {
        m >>= 1u;
        ++offset;
        if (m == 0) {
            break;
        }
    }
    return (raw & mask) >> offset;
}

SMBusPostboxProtocol::CmdStatus SMBusPostbox::waitAndPollStatus(int max_retry, int interval_ms) {
    for (int i = 0; i <= max_retry; ++i) {
        auto temp = readNVGPUReg(NV_CMD_REG);
        if (temp.size() != 4) {
            elog("SMBusPostbox: status read empty");
            return CMD_STATUS_NULL;
        }
        uint32_t status_val = 0;
        for (auto b : temp) {
            status_val = (status_val << 8) | b;
        }
        auto status = static_cast<CmdStatus>((status_val & 0xFF) & NV_GPU_STATUS_MASK);
        if (status != CMD_STATUS_NULL) {
            return status;
        }
        if (i < max_retry) {
            std::this_thread::sleep_for(std::chrono::milliseconds(interval_ms));
        }
    }
    return CMD_STATUS_NULL;
}

std::string SMBusPostbox::sendRequest(const RequestParams& request) {
    // 参数校验（最小化）
    if (request.name.empty()) {
        elog("SMBusPostbox::sendRequest invalid name");
        return "";
    }

    // KEEP_DIGITAL 路径
    if (request.data_type == KEEP_DIGITAL) {
        // 组装并下发命令
        uint32_t cmd = assembleSMBPBICommand(request.opcode, request.arg1, request.arg2);
        if (!writeNVGPUReg(cmd, NV_CMD_REG)) {
            return "";
        }
        // 等待状态
        std::this_thread::sleep_for(std::chrono::milliseconds(100));
        auto status = waitAndPollStatus(20, 1000);
        if (status != CMD_STATUS_SUCCESS) {
            elog("SMBusPostbox: cmd status not success: ${status}", ("status", (int)status));
            return "";
        }
        if (request.data_out) {
            auto res = readNVGPUReg(data_reg_addr_);
            if (res.size() != 4) {
                elog("SMBusPostbox: data read empty");
                return "";
            }
            uint32_t raw = 0;
            for (auto b : res) {
                raw = (raw << 8) | b;
            }
            uint32_t mask = (request.data_mask == 0 ? 0xFFFFFFFFu : request.data_mask);
            uint32_t val  = getMaskedValue(raw, mask);

            (void)request;

            return std::to_string(val);
        }
        return "";
    } else if (request.data_type == KEEP_ASCII || request.data_type == ARRAY_TO_ASCII_BIGENDIAN ||
               request.data_type == ARRAY_TO_ASCII_LITTLEENDIAN) {
        // ASCII 路径：按 data_len 每4字节一段分多次读取（arg2 为段索引）
        if (!request.data_out || request.data_len == 0) {
            elog("SMBusPostbox: ASCII path requires data_out=1 and valid data_len");
            return "";
        }
        uint8_t     data_len   = request.data_len;
        uint8_t     fetch_time = data_len / 4 + (data_len % 4 ? 1 : 0);
        std::string result;
        for (uint8_t i = 0; i < fetch_time; ++i) {
            uint32_t cmd = assembleSMBPBICommand(request.opcode, request.arg1, i);
            if (!writeNVGPUReg(cmd, NV_CMD_REG)) {
                return "";
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            auto status = waitAndPollStatus(20, 1000);
            if (status != CMD_STATUS_SUCCESS) {
                elog("SMBusPostbox: ASCII chunk status not success: ${status}",
                     ("status", (int)status));
                return "";
            }
            auto res = readNVGPUReg(data_reg_addr_);
            if (res.size() != 4) {
                elog("SMBusPostbox: ASCII read empty at chunk=${idx}", ("idx", (int)i));
                return "";
            }
            char buf_be[9] = {0};
            char buf_le[9] = {0};
            snprintf(buf_be, sizeof(buf_be), "%02x%02x%02x%02x", res[0], res[1], res[2], res[3]);
            snprintf(buf_le, sizeof(buf_le), "%02x%02x%02x%02x", res[3], res[2], res[1], res[0]);
            if (request.data_type == KEEP_ASCII) {
                // 拼接原始字节为字符串
                result.append(reinterpret_cast<const char*>(res.data()),
                              strnlen(reinterpret_cast<const char*>(res.data()), res.size()));

            } else if (request.data_type == ARRAY_TO_ASCII_BIGENDIAN) {
                result += std::string(buf_be);
            } else { // LITTLEENDIAN
                result += std::string(buf_le);
            }
        }
        return result;
    } else if (request.data_type == KEEP_ARRAY) {
        // KEEP_ARRAY: ex_data_out 作为 nvlink_num，data_len 作为 item_bits
        const uint16_t MAX_NVLINK_NUM = 256;
        uint16_t       nvlink_num     = static_cast<uint16_t>(request.ex_data_out);
        uint8_t        item_bits      = request.data_len;
        if (nvlink_num == 0 || nvlink_num > MAX_NVLINK_NUM || item_bits == 0 || item_bits > 32) {
            elog("SMBusPostbox: KEEP_ARRAY invalid params nvlink_num=${n}, item_bits=${b}",
                 ("n", (int)nvlink_num)("b", (int)item_bits));
            return "";
        }
        uint32_t       read_bits     = static_cast<uint32_t>(nvlink_num) * item_bits;
        const uint32_t BITS_PER_READ = 32u * 2u; // 每次最多返回两个寄存器(64bit)
        uint32_t       fetch_time    = (read_bits + BITS_PER_READ - 1) / BITS_PER_READ;

        auto push_items = [&](uint32_t data, uint16_t& offset, std::vector<uint32_t>& out) {
            uint32_t data_mask =
                (item_bits == 32) ? 0xFFFFFFFFu : (0xFFFFFFFFu >> (32 - item_bits));
            uint32_t reg_offset = 0;
            while (reg_offset < 32) {
                if (offset >= nvlink_num) {
                    break;
                }
                uint32_t val = (data >> reg_offset) & data_mask;
                out.push_back(val);
                ++offset;
                reg_offset += item_bits;
            }
        };

        std::vector<uint32_t> info_array;
        info_array.reserve(nvlink_num);
        uint16_t info_offset = 0;
        for (uint32_t i = 0; i < fetch_time; ++i) {
            uint32_t cmd =
                assembleSMBPBICommand(request.opcode, request.arg1, static_cast<uint8_t>(i));
            if (!writeNVGPUReg(cmd, NV_CMD_REG)) {
                return "";
            }
            std::this_thread::sleep_for(std::chrono::milliseconds(100));
            auto status = waitAndPollStatus(20, 1000);
            if (status != CMD_STATUS_SUCCESS) {
                elog("SMBusPostbox: KEEP_ARRAY chunk status not success: ${status}",
                     ("status", (int)status));
                return "";
            }
            auto res = readNVGPUReg(data_reg_addr_);
            if (res.size() == 4) {
                uint32_t data = (res[0] << 24) | (res[1] << 16) | (res[2] << 8) | res[3];
                push_items(data, info_offset, info_array);
            }
            auto res_ex = readNVGPUReg(static_cast<uint8_t>(data_reg_addr_ + 1));
            if (res_ex.size() == 4) {
                uint32_t data =
                    (res_ex[0] << 24) | (res_ex[1] << 16) | (res_ex[2] << 8) | res_ex[3];
                push_items(data, info_offset, info_array);
            }
            if (info_offset >= nvlink_num) {
                break;
            }
        }
        if (info_array.size() > nvlink_num) {
            info_array.resize(nvlink_num);
        }
        // 返回完整数组拼接成的连续字符串（将每个元素按低8位视为一个字符）
        if (info_array.empty()) {
            return "";
        }
        std::string out;
        out.reserve(info_array.size());
        for (auto v : info_array) {
            out.push_back(static_cast<char>(v & 0xFF));
        }
        return out;
    } else {
        elog("SMBusPostbox: unsupported data_type=${type}", ("type", (int)request.data_type));
        return "";
    }
}

} // namespace dev